﻿using System;
using System.ComponentModel.Composition;
using Cubicle.Core.Interfaces;
using Cubicle.Core.Processors.Parameter;

namespace Cubicle.Core.Processors.Invoke
{
    [Export("Cubicle.Processors.Invoke.SetPropertyProcessor", typeof(IProcessor<InstructionRequest>))]
    [ExportMetadata("Name", "SetPropertyProcessor")]
    [ExportMetadata("Description", "Peforms 'set' operations on a target property")]
    public class SetPropertyProcessor : InstructionRequestProcessor
    {
        [Import]
        private ParameterProcessor _paramProcessor;

        [ImportingConstructor]
        public SetPropertyProcessor() : base(RequestType.Virtual | RequestType.Static | RequestType.Property | RequestType.Set) { }

        #region Overrides of Processor<InstructionRequest>

        // When setting indexed properties the processor expects the first parameters 
        // to be the indexes and the last parameter to be the value
        public override InstructionRequest ConditionalProcess(InstructionRequest request)
        {
            if (request.TargetProperty == null)
            {
                request.Fail("No property to set.");
                return request;
            }

            var parameters = request.Inst.Parameters;
            var indexers = request.TargetProperty.GetIndexParameters();

            if (indexers.Length != parameters.Length-1)
            {
                request.Fail("Inconsistent number of parameters supplied for indexed property. Supplied = {0}, Expected = {1}", indexers.Length, parameters.Length-1);
                return request;
            }

            var targetTypes = new Type[parameters.Length];
            targetTypes[parameters.Length-1] = request.TargetProperty.PropertyType;
            for (int i = 0; i < indexers.Length; i++)
                targetTypes[i] = indexers[i].ParameterType;

            var tempResult = targetTypes.Process<object>(parameters, _paramProcessor);

            object[] resolvedIndexes = null;
            if (indexers.Length > 0)
            {
                resolvedIndexes = new object[parameters.Length - 1];
                Array.ConstrainedCopy(tempResult, 0, resolvedIndexes, 0, resolvedIndexes.Length);
            }

            // Before accessor event
            var args = new InstructionEventArgs(InstructionEventType.BeforeAccessor, request);
            RaiseProcessorEvent(this, args);

            request.TargetProperty.SetValue(request.Target, tempResult[tempResult.Length-1], resolvedIndexes);

            // After accessor event
            args = new InstructionEventArgs(InstructionEventType.AfterAccessor, request);
            RaiseProcessorEvent(this, args);

            request.Complete(null);
            return request;
        }

        #endregion
    }
}